#include "Mapa.h"
#include "Casillero.h"
#include "Coordenada.h"
#include "IOcupante.h"
#include "IMovible.h"
#include "Aliado.h"
#include <vector>
//#define TAM_MAPA

using std::vector;

/* Para crear el mapa voy a necesitar el tamaño y qué hay en cada casillero.
 * El tamaño puede ser fijo, pero los elementos de cada casillero se van a leer de XML.
 * Para cada posicion leida del XML voy a crear un casillero con el tipo de MateriaFisico
 * correspondiente. */
Mapa::Mapa(){
    tamanio = 0;
    //cuadricula =  vector<vector<Casillero*> >();
}

Mapa::Mapa(int tam){
    tamanio = tam;
    //cuadricula =  vector<vector<Casillero*> >();
}

Mapa::~Mapa(){
    if (!cuadricula.empty()) {
        for(unsigned int i = 0; i < cuadricula.size(); i++) {
            while (!cuadricula[i].empty()) {
                Casillero* aux = (cuadricula[i].back());
                cuadricula[i].pop_back();
                delete aux;
            }
        }
    }
}

void Mapa::inicializar() {
    for(int i = 0; i < tamanio; i++) {
        vector<Casillero*> fila = vector<Casillero*>();
        for (int j = 0; j < tamanio; j++) {
            Coordenada coord = Coordenada(i,j);
            Casillero* casillero = new Casillero(coord);
            fila.push_back(casillero);
        }
        cuadricula.push_back(fila);
    }
}


bool Mapa::fueraDeRango(Coordenada nuevaCoord) {
    int x = nuevaCoord.getX();
    int y = nuevaCoord.getY();
    if (x < 0 || y < 0 || x >= tamanio || y >= tamanio)
        return true;
    return false;
}


bool Mapa::posicionBloqueada(Coordenada nuevaCoord) {
    int x = nuevaCoord.getX();
    int y = nuevaCoord.getY();
    Casillero* casillero = cuadricula[x][y];
    if ((casillero->getMaterial())->permiteDesplazamiento() == false)
        return true;
    return false;
}



bool Mapa::moverElemento(IMovible* movible){   
    movible->mover();
    Coordenada coord = movible->getPosicion();
    /* Validar Movimiento: 
     * 1) Veo que sea una coordenada valida (que no sea negativa ni sea mas grande que el mapa).*/
    /* 2) Veo que sea un casillero donde se pueda posicionar, es decir, que no haya ni metal ni caja.*/
    if ((fueraDeRango(coord)) || (posicionBloqueada(coord))) {
        movible->retroceder();
        return false;
    }
    this->ubicarElemento(movible, coord);
    return true;
}

bool Mapa::hayColision(Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    Casillero* casillero = cuadricula[x][y];
    if (casillero->estaVacio())
        return false;
    if (casillero->getOcupantes().size() == 1)
        return false;
    return true;
}

void Mapa::resolverColisiones(IMovible* movible) {
    Coordenada coord = movible->getPosicion();
    int x = coord.getX();
    int y = coord.getY();
    Casillero* casillero = cuadricula[x][y];
    /* Colisionar con todos los personajes que se encuentre. */
    for (unsigned int i = 0; i < (casillero->getOcupantes()).size(); i++){
            //hace que movible colisione con cada uno de los ocupantes del casillero:
        if (movible != casillero->getOcupantes()[i])
            movible->colisionarCon((casillero->getOcupantes())[i]);
    }
}


void Mapa::establecerMaterial(MaterialFisico* material, Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    cuadricula[x][y]->setMaterial(material);
}

void Mapa::ubicarElemento(IOcupante* elemento, Coordenada coord){
    int x = coord.getX();
    int y = coord.getY();
    cuadricula[x][y]->agregarOcupante(elemento);
}

void Mapa::sacarElemento(IOcupante* elemento, Coordenada coord){
    int x = coord.getX();
    int y = coord.getY();
    cuadricula[x][y]->sacarOcupante(elemento);
}

IOcupante* Mapa::obtenerOcupante(Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    try {
        return cuadricula[x][y]->getOcupantes()[0];
    } catch (const char* e) {
        return NULL;
    }
}

MaterialFisico* Mapa::obtenerMaterial(Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    return cuadricula[x][y]->getMaterial();
}

int Mapa::getTamanio() {
    return tamanio;
}
